﻿/**
* 
* Copyright (c) 2012 Anders Høst Kjærgaard | ahkj@itu.dk
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, 
* including without limitation the rights to use, copy, modify, merge, publish, distribute, 
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial 
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

using System;
using System.Linq;
using Flatland.GeneticProgramming.CoreTypes;
using Flatland.Utilities;

namespace Flatland.GeneticProgramming.SyntaxTree {
    /// <summary>
    /// Tree with nodes of type Function
    /// </summary>
    [Serializable]
    public abstract class Tree : ICloneable {
        public Tree Parent { get; set; }
        protected Tree() { }

        /// <summary>
        /// Counts the total number of nodes in the tree.
        /// </summary>
        /// <returns>Returns the number of nodes</returns>
        public int CountNodes() {
            return _CountNodes(this);
        }

        private int _CountNodes(Tree tree) {
            int sum = 1;
            if (tree.GetType() == typeof(Node))
                sum += ((Node)tree).Children.Sum(child => _CountNodes(child));
            return sum;
        }

        /// <summary>
        /// Get the maximum depth the tree from a given node.
        /// </summary>
        /// <returns>The maximum depth from this node to a leaf</returns>
        public int MaxDetpth() {
            return _MaxDepth(this, 0);
        }

        private int _MaxDepth(Tree tree, int depth) {
            if (tree.GetType() == typeof(Node))
                return 1 + ((Node)tree).Children.Select(n => _MaxDepth(n, depth)).Max();
            return 1;
        }

        /// <summary>
        /// Uses 50 percent chance of chosing a node, or a child recursivly
        /// </summary>
        /// <returns></returns>
        public Tree GetRandomNode() {
            return _GetRandonNode(this);
        }

        private Tree _GetRandonNode(Tree tree) {
            if (Utils.Random.Next(2) == 0 || tree.GetType() == typeof(Leaf)) return tree;
            return _GetRandonNode(((Node)tree).Children[Utils.Random.Next(((Node)tree).Children.Count)]);
        }

        /// <summary>
        /// Print details to the console of a tree of type Tree.
        /// </summary>
        public void PrintTree() {
            Console.WriteLine("Depth: {0} - {1}", 1, this);
            _PrintTree(this, 1);
        }

        private void _PrintTree(Tree tree, uint depth) {
            if (tree.GetType() == typeof(Node)) {
                foreach (Tree child in ((Node)tree).Children) {
                    Console.WriteLine("Depth: {0} - {1}", depth + 1, child);
                }
                foreach (Tree child in ((Node)tree).Children) {
                    _PrintTree(child, depth + 1);
                }
            }
        }


        /// <summary>
        /// Return the nested depth of a Tree node
        /// </summary>
        /// <returns></returns>
        public int Depth() {
            int depth = 1;
            Tree parent = Parent;
            while (parent != null) {
                parent = parent.Parent;
                depth++;
            }
            return depth;
        }

        
        /// <summary>
        /// Clone the tree from a given node.
        /// </summary>
        /// <returns></returns>
        public abstract object Clone();

        /// <summary>
        /// True if the primitive is member of the command set
        /// </summary>
        /// <returns></returns>
        public abstract bool IsCommandSet();

        /// <summary>
        /// Execute the genetic code subject to the tree structure.
        /// </summary>
        /// <param name="genoType"></param>
        /// <returns></returns>
        public abstract object Execute(GenoType genoType);
    }
}
